#pragma once
#include "CSyntax.h"

class CType;

/*class CParamType
{
	tParamType	m_BasicType;
	eArgFlag	m_ArgFlags;
	BYTE		m_Size;
	WORD		m_ExtendedType;

	inline int CalcSize() const
	{
		if(m_Size) return m_Size;
		if(IsVarArg()) return MAX_PARAMS;
		return m_Size;
	}

public:
	CParamType()
	{
		m_BasicType = 0;
		m_ArgFlags = ARGFLAG_NONE;
		m_ExtendedType = 0;
		m_Size = 0;
	}

	CParamType(tParamType bBasicType, eArgFlag argflags = ARGFLAG_NONE, BYTE bSize = 0)
	{
		m_BasicType = bBasicType;
		m_ArgFlags = argflags;
		m_Size = bSize;
	}

	inline const char * GetBasicName() const
	{
		return CSyntax::GetBasicTypeName(m_BasicType);
	}

	void BuildFromString(char *szIdentifier)
	{
		if(m_ExtendedType = CSyntax::GetExtendedTypeByIdentifier(*szIdentifier))
		{
			m_BasicType = CSyntax::GetBasicTypeByIdentifier(*szIdentifier++);

			if(m_ExtendedType & ET_NUM_EXTRA_PARAMS || m_ExtendedType & ET_VAR_EXTRA_PARAMS)
				m_Size = StringToInt(szIdentifier, szIdentifier);

			if(*szIdentifier)
			{
				if(m_ExtendedType |= CSyntax::GetExtendedTypeByIdentifier(*szIdentifier))
					szIdentifier++;
				if(*szIdentifier)
					m_BasicType |= CSyntax::GetBasicTypeByIdentifier(*szIdentifier++);
			}
			else m_BasicType = CSyntax::GetBasicTypeForExtendedType(m_ExtendedType);
		}
		else
		{
			m_BasicType = CSyntax::GetBasicTypeByIdentifier(*szIdentifier++);
		
			switch(m_BasicType)
			{
			default:
				break;
			case PT_CONST:
				{
					tParamType t = CSyntax::GetBasicTypeByIdentifier(*szIdentifier);
					if(t) m_BasicType |= t;
					else m_BasicType |= PT_INT;
					break;
				}
			case PT_VAR:
			case PT_LVAR:
				m_BasicType |= CSyntax::GetBasicTypeByIdentifier(*szIdentifier);
				break;
			}
		}
	}

	inline bool IsNumericCompatible(const CParamType &type) const		{return IsNumeric() && type.IsNumeric();}
	inline bool IsVarCompatible(const CParamType &type) const			{return (m_BasicType & PT_ANYVAR) == (type.m_BasicType & PT_ANYVAR);}
	inline bool IsValCompatible(const CParamType &type) const			{return (m_BasicType & PT_ANYVALUE) == (type.m_BasicType & PT_ANYVALUE);}
	//inline bool IsBasicallyCompatible(const CParamType &type) const		{return (m_BasicType & PT_ANYBASIC) == (type.m_BasicType & PT_ANYBASIC);}

	bool IsCompatible(const CParamType &type) const
	{
		// If it's ambiguous, whatever...
		if(IsAmbiguous()) return true;

		// If it's JUST a "VAR" or "LVAR", we specifically want to match the var types
		if((IsAnyVar() && GetBasicType() == PT_NONE) || (type.IsAnyVar() && type.GetBasicType() == PT_NONE))
			return IsVarCompatible(type);
		else
		{
			// Otherwise, we can just match the value types INT/FLOAT/TEXT_LABEL/STRING
			if(IsValCompatible(type))
			{
				// Simplify some extended types just to be sure we don't h
				switch(m_ExtendedType)
				{
				case ET_NUM_EXTRA_PARAMS:
					return type.m_BasicType & PT_INT;
				case ET_LABEL:
					return type.m_BasicType & PT_INT;
				case ET_BOOLEAN:
					return type.m_BasicType & PT_INT;
				case ET_MODEL:
					return (type.m_BasicType & PT_CONST) != 0;
				default:
					return true;
				}
			}
		}
		return false;
	}

	bool IsStrictlyCompatible(const CParamType &type) const
	{
		if(IsAmbiguous()) return true;
		return m_ExtendedType == type.m_ExtendedType;
		
		if(IsValCompatible(type) && IsVarCompatible(type))
			return (m_ExtendedType & ~ET_AMBIGUOUS) == (type.m_ExtendedType & ~ET_AMBIGUOUS);
		return false;
	}

	int GetCompatibilityScore(const CParamType &type) const
	{
		if(IsStrictlyCompatible(type)) return 2;
		else if(IsCompatible(type)) return 1;
		return 0;
	}

	tParamType AddBasicType(tParamType ptype)
	{
		CParamType type(ptype);
		if(!IsValCompatible(type) && !IsVarCompatible(type)) return m_BasicType |= ptype;
		return false;
	}

	WORD AddExtendedType(WORD etype)
	{
		CParamType type(CSyntax::GetBasicTypeForExtendedType(etype));
		type.m_ExtendedType |= etype;
		if(IsCompatible(type)) return m_ExtendedType |= etype;
		return false;
	}

	inline void	SetExtendedType(WORD et)				{m_ExtendedType = et;}

	inline void ClearBasicType()
	{
		m_BasicType = PT_NONE;
	}

	inline tParamType GetBasicType() const				{return m_BasicType & PT_ANYBASIC;}
	inline WORD GetExtendedType() const					{return m_ExtendedType;}
	BYTE GetSize()										{return m_Size ? m_Size : m_Size = CalcSize();}
	BYTE GetSize() const								{return m_Size ? m_Size : CalcSize();}

	inline bool IsBasic() const							{return (m_BasicType & PT_ANYBASIC) != PT_NONE;}
	inline bool IsNumeric() const						{return (m_BasicType & PT_NUMBER) != PT_NONE;}
	inline bool IsConst() const							{return (m_BasicType & PT_CONST) != PT_NONE;}
	inline bool IsVar() const							{return (m_BasicType & PT_VAR) != PT_NONE;}
	inline bool IsLVar() const							{return (m_BasicType & PT_LVAR) != PT_NONE;}
	inline bool IsAnyVar() const						{return (m_BasicType & PT_ANYVAR) != PT_NONE;}
	inline bool IsAnyArray() const						{return (m_BasicType & PT_ARRAY) != PT_NONE;}

	inline bool IsLabel() const							{return (m_ExtendedType & ET_LABEL) != ET_NONE;}
	inline bool IsModel() const							{return (m_ExtendedType & ET_MODEL) != ET_NONE;}
	inline bool IsParsable() const						{return (m_ExtendedType & ET_PARSABLE) != ET_NONE;}
	inline bool IsAmbiguous() const						{return (m_ExtendedType & ET_AMBIGUOUS) != ET_NONE;}
	inline bool IsUnknown() const						{return (m_ExtendedType & ET_UNKNOWN_PARAM) != ET_NONE;}
	inline bool IsVarArg() const						{return (m_ExtendedType & (ET_VAR_EXTRA_PARAMS | ET_NUM_EXTRA_PARAMS)) != ET_NONE;}
	inline bool IsNumArgs() const						{return (m_ExtendedType & ET_NUM_EXTRA_PARAMS) != ET_NONE;}
};*/

class CTranslation
{
	const CTypeBase		*	m_pType;
	const CTypeBase		*	m_pArrayIndexType;
	short					m_nNumConditions;
	short					m_nNumElements;
	short					m_nSize;
	bool					m_bIsArray;
	bool					m_bPromotedBySubScript;

	class
	{
		eTranslateCondition		m_eCond;
		int						m_nVal;
		bool					m_bConstSize;

	public:
		inline void Set(eTranslateCondition eCond, int nVal, bool bConstSize = false)
		{
			m_eCond = eCond;
			m_nVal = nVal;
			m_bConstSize = bConstSize;
		}
		inline eTranslateCondition		GetCondition() const		{return m_eCond;}
		inline int						GetValue() const			{return m_nVal;}
		inline bool						IsSizeConst() const			{return m_bConstSize;}
	} m_Conditions[MAX_TRANSLATION_CONDITIONS];
	class
	{
		eTranslateElement		m_eType;
		int						m_nVal;

	public:
		void Set(eTranslateElement eType, int nVal)
		{
			m_eType = eType;
			m_nVal = nVal;
		}

		inline eTranslateElement		GetType() const				{return m_eType;}
		inline int						GetValue() const			{return m_nVal;}
	} m_Elements[MAX_TRANSLATION_ELEMENTS];

public:
	CTranslation(const CTypeBase * pType, const CTypeBase * pIndexType = nullptr)
		:	m_pType(pType),
			m_pArrayIndexType(pIndexType),
			m_nNumConditions(0),
			m_nNumElements(0),
			m_nSize(0),
			m_bIsArray(pIndexType != nullptr),
			m_bPromotedBySubScript(false)
	{
	}
	
	inline bool					IsArray() const						{return m_bIsArray;}
	inline bool					IsSubScriptPromoted() const			{return m_bPromotedBySubScript;}
	inline const CTypeBase	*	GetArrayIndexType() const			{return m_pArrayIndexType;}
	inline int					GetNumConditions() const			{return m_nNumConditions;}
	inline int					GetNumElements() const				{return m_nNumElements;}

	inline void					PromoteBySubScript(const CTypeBase * pType)
	{
		if(!m_bPromotedBySubScript)
		{
			m_bPromotedBySubScript = true;
			m_pType = pType;
		}
	}

	inline void					AddCondition(eTranslateCondition eCondition, int value, bool bConstSize = false)
	{
		m_Conditions[m_nNumConditions++].Set(eCondition, value, bConstSize);
	}
	inline void					AddElement(eTranslateElement eType, int value)
	{
		m_Elements[m_nNumElements++].Set(eType, value);
	}
	inline eTranslateCondition	GetCondition(int idx) const
	{
		return idx < m_nNumConditions ? m_Conditions[idx].GetCondition() : TRANSLATE_COND_NONE;
	}
	inline int					GetConditionValue(int idx) const
	{
		return idx < m_nNumConditions ? m_Conditions[idx].GetValue() : TRANSLATE_COND_NONE;
	}
	inline eDataType			GetDataType(int idx) const
	{
		return (eDataType)GetConditionValue(idx);
	}
	inline int					GetStringSize(int idx) const
	{
		return GetConditionValue(idx);
	}
	inline bool					IsConditionSizeConst(int idx) const
	{
		return idx < m_nNumConditions ? m_Conditions[idx].IsSizeConst() : false;
	}
	inline eTranslateElement	GetElement(int idx) const
	{
		return idx < m_nNumElements ? m_Elements[idx].GetType() : TRANSLATE_ELEM_NONE;
	}
	inline int					GetElementValue(int idx) const
	{
		return idx < m_nNumElements ? m_Elements[idx].GetValue() : TRANSLATE_ELEM_NONE;
	}
	/*short						Translate(int valsize)
	{
		for(int i=0; i<m_nNumElements; ++i)
		{
			switch(m_Elements[i].GetType())
			{
			case TRANSLATE_ELEM_VAL:
				return GetIntNumBytes(m_Elements[i].GetValue());
			case TRANSLATE_ELEM_COND:
				switch(m_Elements[i].GetValue())
				{
				case TRANSLATE_COND_SIZEOF_VAL: return valsize;

				}
			}
		}
	}*/
};

class CTypeBase : public CIdentifier
{
	friend class CSyntax;

protected:
	char							m_szID[16];
	PType							m_BasicType;
	eTokenType						m_eTokenType;
	int								m_nNumTranslations;
	CLListEx<const CTranslation*>	m_Translations;

	CTypeBase(const char * id, const char * name, PType bBasicType, eTokenType toktype)
		:	CIdentifier(name),
			m_BasicType(bBasicType),
			m_eTokenType(toktype),
			m_nNumTranslations(0)
	{
		szncpy(m_szID, id, sizeof(m_szID));
	}

public:
	virtual bool									IsExtended() const=0;
	virtual const class CExtendedType			*	Extend() const=0;
	virtual const class CType					*	Standardise() const=0;

	void											AddTranslation(const CTranslation* p)	{m_Translations.Add(p); ++m_nNumTranslations;}
	bool											IsID(const char * szID)	const		{return !stricmp(szID, m_szID);}
	const char									*	GetID() const						{return m_szID;}
	const CLListEx<const CTranslation*>			&	GetTranslations() const 			{return m_Translations;}
	PType											GetType() const						{return m_BasicType;}
	PType											GetBasicType() const				{return PType(m_BasicType & PT_ANYBASIC);}
	PType											GetVarType() const					{return PType(m_BasicType & PT_ANYVAR);}
	PType											GetArrayType() const				{return PType(m_BasicType & PT_ANYARRAY);}
	PType											GetNumericType() const				{return PType(m_BasicType & PT_NUMBER);}
	eTokenType										GetTokenType() const				{return m_eTokenType;}
	int												GetNumTranslations() const			{return m_nNumTranslations;}
	
	bool											IsInt() const						{return (m_BasicType & PT_INT) != PT_NONE;}
	bool											IsFloat() const						{return (m_BasicType & PT_FLOAT) != PT_NONE;}
	bool											IsTextLabel() const					{return (m_BasicType & PT_TEXT_LABEL) != PT_NONE;}
	bool											IsString() const					{return (m_BasicType & PT_STRING) != PT_NONE;}
	bool											IsVar() const						{return (m_BasicType & PT_VAR) != PT_NONE;}
	bool											IsLVar() const						{return (m_BasicType & PT_LVAR) != PT_NONE;}
	bool											IsJustAnyVar() const				{return (m_BasicType & PT_ANYVAR) == m_BasicType;}
	bool											IsJustAnyVal() const				{return (m_BasicType & PT_ANYBASIC) == m_BasicType;}
	bool											IsAnyVar() const					{return (m_BasicType & PT_ANYVAR) != PT_NONE;}
	bool											IsAnyVal() const					{return (m_BasicType & PT_ANYBASIC) != PT_NONE;}
	bool											IsAnyArray() const					{return (m_BasicType & PT_ANYARRAY) != PT_NONE;}
};

class CType : public CTypeBase
{
	friend class CSyntax;
	TypeIndex					m_nInternalIdx;
	const CType				*	m_pStd;					// standardisation
	bool						m_bIsPseudo : 1;

	CType(TypeIndex idx, const CType * pStd = nullptr)
		:	CTypeBase(CSyntax::GetTypeID(idx), CSyntax::GetTypeName(idx), CSyntax::GetBasicType(idx), CSyntax::GetTokenType(idx)),
			m_nInternalIdx(idx),
			m_pStd(CSyntax::IsTypePseudo(idx) && pStd && !pStd->IsPseudo() ? pStd : nullptr),
			m_bIsPseudo(CSyntax::IsTypePseudo(idx))
	{
	}

	~CType()
	{
	}
	
	bool											IsExtended() const override			{return false;}
	const class CExtendedType					*	Extend() const override				{return nullptr;}

public:
	bool											IsPseudo() const					{return m_bIsPseudo;}
	TypeIndex										GetInternalID() const				{return m_nInternalIdx;}

	const class CType							*	Standardise() const override		{return m_pStd ? m_pStd : this;}
};
class CExtendedType : public CTypeBase
{
	friend class CSyntax;
	ExtendedTypeIndex		m_uInternalIdx;
	const class CEnum	*	m_pEnum;
	const CType			*	m_pStd;

	CExtendedType(ExtendedTypeIndex idx, const CType * pStd)
		:	CTypeBase(CSyntax::GetTypeID(idx), CSyntax::GetTypeName(idx), CSyntax::GetBasicType(idx), CSyntax::GetTokenType(idx)),
			m_uInternalIdx(idx),
			m_pEnum(nullptr),
			m_pStd(pStd)
	{
	}
	CExtendedType(ExtendedTypeIndex idx, const char *id, const CType * pStd, const char * szName, eTokenType toktype)
		:	CTypeBase(id, szName, pStd->GetType(), toktype),
			m_uInternalIdx(idx),
			m_pEnum(nullptr),
			m_pStd(pStd)
	{
	}
	CExtendedType(const char *id, const CType * pStd, const CEnum * pEnum);

	bool									IsExtended() const					{return true;}
	const CExtendedType					*	Extend() const						{return this;}
	
public:
	ExtendedTypeIndex						GetInternalID() const				{return m_uInternalIdx;}
	const CType							*	Standardise() const					{return m_pStd;}
	const class CEnum					*	GetEnum() const						{return m_pEnum;}
};